De computertaal Python werd ooorspronkelijk ontworpen als een open source computertaal die voor iedereen makkelijk te leren en te programmeren zou zijn. Dat verklaart de enorme populariteit ervan. Maar door die enorme populariteit is het in de loop der tijd toch ook een heel complexe taal geworden. Er zijn eindeloos veel boeken verschenen over Python. De inhoud van deze boeken wil ik niet herhalen. Deze pagina gaat ervanuit dat je met de eerste beginselen van de taal al kennisgemaakt hebt. Op deze webpagina verzamel ik allerlei weetjes die ik makkelijk wil kunnen terugvinden.
Met de functie input() kun je iemand iets laten intypen. Wat de gebruiker intikt wordt altijd als string geïnterpreteerd. Mocht je willen dat wat ingetikt wordt anders wordt opgevat, moet je het ingetikte converteren.
def main(): x = input('x = ') print(type(x)) if x.isnumeric(): x = int(x) print(type(x)) if __name__ == '__main__': main()
Python kent verschillende soorten gegevens. De meest bekende zijn:
letters | characters | str() |
gehele getallen | integers | int() |
drijvende-komma-getallen | floating point numbers | float() |
complexe getallen | complex numbers | |
waar of niet waar | Booleans |
In Python wordt geen decimale komma gebruikt, maar een decimale punt.
def main(): x = 'a' print( ord(x) ) # 97 print( x.encode('utf-8') ) # b'a' print( bytes(x, 'utf-8') ) # b'a' print( hex(ord(x)) ) # 0x61 print( x.encode('utf-8').hex() ) # 61 print( bytes(x, 'utf-8').hex() ) # 61 n = 97 print( chr(n) ) # a b = b'\x61' print( str(b, 'utf-8') ) # a print( b.decode('utf-8') ) # a if __name__ == '__main__': main()
Een bestand kun je met de volgende code aanmaken:
def main(): file_object = open('outfile.csv', 'w') file_object.write("Hello\n") file_object.close() if __name__ == '__main__': main()
Voor mijn privé-programma's, vind ik csv-bestanden inlezen de gemakkelijkste vorm van invoer. Csv-bestanden kun je makkelijk aanmaken met Kladblok of Excel. Mijn voorkeur gaat uit naar de puntkomma als scheidingsteken. Het inlezen van een bestand kan in Python op verschillende manieren. De eerste manier is door een bestand te openen voor lezen, vervolgens het bestand te doorlopen met een for-loop. Bij een csv-bestand kun je in die for-loop elk record splitsen in verschillende velden. Na verwerking moet je het bestand weer te sluiten.
def main(): file_object = open('myfile.csv', 'r') for line in file_object: print(line.strip('\n')) velden = line.split(';') for v in velden: print(v) file_object.close() if __name__ == '__main__': main()
Een andere manier om een bestand in te lezen gaat met behulp van een with-context. Daarbij hoef je de file niet te sluiten, want de context van het with-statement zorgt ervoor dat dat gebeurt :
import sys def main(): filename = 'MyFile.txt' try: with open(filename) as f_input: for line in f_input: line = line.strip('\n') print(line) except Exception as err: print('(1) err') print( err ) print('(2) sys.exc_info()[0]') print( sys.exc_info()[0] ) # exception class print('(3) sys.exc_info()[1]') print( sys.exc_info()[1] ) # value print('(4) sys.exc_info()[2]') print( sys.exc_info()[2] ) # traceback object print('=====') if __name__ == '__main__': main()
Als het bestand MyFile.txt niet bestaat, geeft het programma de volgende output:
(1) err [Errno 2] No such file or directory: 'MyFile.txt' (2) sys.exc_info()[0] <class 'FileNotFoundError'> (3) sys.exc_info()[1] [Errno 2] No such file or directory: 'MyFile.txt' (4) sys.exc_info()[2] <traceback object at 0x000002C6B50EB200> =====
Je kunt het afhandelen vvan de fout in bovenstaand programma wat eleganter laten verlopen door de de class die in bovenstaande foutmelding werd genoemd, afzonderlijk af te handelen:
import sys def main(): filename = 'MyFile.txt' try: with open(filename) as f_input: for line in f_input: line = line.strip('\n') print(line) except FileNotFoundError: print('Bestand ' + filename + ' is niet aanwezig') except Exception as err: print('(1) err') print( err ) print('(2) sys.exc_info()[0]') print( sys.exc_info()[0] ) # exception class print('(3) sys.exc_info()[1]') print( sys.exc_info()[1] ) # value print('(4) sys.exc_info()[2]') print( sys.exc_info()[2] ) # traceback object print('=====') if __name__ == '__main__': main()
Als je de html-file die gaat over CSS, als invoerbestand neemt, wordt het programma maar deels uitgevoerd. Het eindigt met de volgende regels:
Als de viewport breed genoeg is, staan de verschillende blokken naast elkaar. (1) err 'charmap' codec can't decode byte 0x9d in position 3699: character maps to(2) sys.exc_info()[0] (3) sys.exc_info()[1] 'charmap' codec can't decode byte 0x9d in position 3699: character maps to (4) sys.exc_info()[2] <traceback object at 0x000001FA93C32E80> =====
Deze informatie is niet voldoende om te weten wat er aan de hand is. We breiden de code uit:
import sys, traceback def main(): filename = 'MyFile.txt' try: with open(filename) as f_input: for line in f_input: line = line.strip('\n') print(line) except FileNotFoundError: print('Bestand ' + filename + ' is niet aanwezig') except Exception as err: print('(1) err') print( err ) print('(2) sys.exc_info()[0]') print( sys.exc_info()[0] ) # exception class print('(3) sys.exc_info()[1]') print( sys.exc_info()[1] ) # value print('(4) sys.exc_info()[2]') print( sys.exc_info()[2] ) # traceback object print('(5) err.object') print( err.object ) print('(6) traceback.print_exception(err)') traceback.print_exception(err) print('(7) traceback.print_tb(err)') traceback.print_tb(err) # traceback print('=====') if __name__ == '__main__': main()
De output wordt:
Als de viewport breed genoeg is, staan de verschillende blokken naast elkaar. (1) err 'charmap' codec can't decode byte 0x9d in position 3699: character maps to <undefined> (2) sys.exc_info()[0] <class 'UnicodeDecodeError'> (3) sys.exc_info()[1] 'charmap' codec can't decode byte 0x9d in position 3699: character maps to <undefined> (4) sys.exc_info()[2] <traceback object at 0x0000022A10433940> (5) err.object Squeezed text (64 lines). (6) traceback.print_exception(err) Traceback (most recent call last): File "E:\Site\Site20240516\prive\ict\extra\test0001.py", line 7, in main for line in f_input: File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\encodings\cp1252.py", line 23, in decode return codecs.charmap_decode(input,self.errors,decoding_table)[0] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3699: character maps to <undefined> (7) traceback.print_tb(err) Traceback (most recent call last): File "E:\Site\Site20240516\prive\ict\extra\test0001.py", line 7, in main for line in f_input: File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\encodings\cp1252.py", line 23, in decode return codecs.charmap_decode(input,self.errors,decoding_table)[0] UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 3699: character maps to <undefined> During handling of the above exception, another exception occurred: Traceback (most recent call last): File "E:\Site\Site20240516\prive\ict\extra\test0001.py", line 29, in <module> main() File "E:\Site\Site20240516\prive\ict\extra\test0001.py", line 26, in main traceback.print_tb(err) # traceback File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\traceback.py", line 55, in print_tb print_list(extract_tb(tb, limit=limit), file=file) File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\traceback.py", line 74, in extract_tb return StackSummary._extract_from_extended_frame_gen( File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\traceback.py", line 418, in _extract_from_extended_frame_gen for f, (lineno, end_lineno, colno, end_colno) in frame_gen: File "C:\Users\Wim\AppData\Local\Programs\Python\Python312\Lib\traceback.py", line 355, in _walk_tb_with_full_positions positions = _get_code_position(tb.tb_frame.f_code, tb.tb_lasti) AttributeError: 'UnicodeDecodeError' object has no attribute 'tb_frame'
Deze foutmelding is te complex voor mij. Ik ga op zoek op Internet, en ontdek dat anderen al meer dan 6 jaar eerder met deze foutmelding worstelden. Als mogelijke oorzaak wordt geopperd dat het bestand niet met utf-8 is aangemaakt. Ik heb de input-file aangemaakt met behulp van het Kladblok-programma in Windows. Windows schijnt niet met utf-8 te werken. De suggesties die op stackoverklow.com worden gegeven, zijn:
De eerste suggestie leidt tot:
import sys, traceback def main(): filename = 'MyFile.txt' tel = 0 try: with open(filename, 'rb') as f_input: for b_line in f_input: line = b_line.decode('utf-8') line = line.strip('\n') print(line) except FileNotFoundError: print('Bestand ' + filename + ' is niet aanwezig') except Exception as err: print('(1) err') print( err ) print('(2) sys.exc_info()[0]') print( sys.exc_info()[0] ) # exception class print('(3) sys.exc_info()[1]') print( sys.exc_info()[1] ) # value print('(4) sys.exc_info()[2]') print( sys.exc_info()[2] ) # traceback object print('(5) err.object') print( err.object ) print('(6) traceback.print_exception(err)') traceback.print_exception(err) print('(7) traceback.print_tb(err)') traceback.print_tb(err) # traceback print('=====') if __name__ == '__main__': main()
Het volgende programma telt een aantal getallen bij elkaar op.
def main(): numbers = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29] quantity = len(numbers) total = 0 i = 0 while i < quantity: total = total + numbers[i] i = i + 1 print(quantity) print(total) if __name__ == '__main__': main()
➋ In het while-statement wordt de voorwaarde 'i < quantity' getest. Er zijn twee plaatsen die voorafgaand aan deze test kunnen worden uitgevoerd. Beide zorgen ervoor dat de voorwaarde het juiste resultaat oplevert. Dat zijn ➊ het statement 'i = 0' voorafgaand aan het while-statement en ➌ het statement 'i = i + 1' aan het einde van block dat wordt uitgevoerd als de voorwaarde True oplevert.
Ik wil bepaalde plaatjes maken met SVG. Ik zie in mijn fanatsieën allerlei plaatjes met rechthoeken die verbonden worden door pijlen. Als ik veel van dergelijke plaatjes aan mijn website wil toevoegen, wil ik een programma dat mij daarbij helpt. Ik ga kijken of het mij lukt zo'n programma te maken.
Een html-file met een svg-plaatje met een rechthoek kun je als volgt maken:
def main(): svg_tekst="the quick brown fox jumps over the lazy dog" html_title = "SVG rechthoek met tekst" file_object = open("outfile.html", "w") file_object.write("<!DOCTYPE html>\n") file_object.write("<html>\n") file_object.write(4*" " + "<head>\n") file_object.write(8*" " + "<title>") file_object.write(html_title) file_object.write("</title>\n") file_object.write(4*" " + "</head>\n") file_object.write(4*" " + "<body>\n") file_object.write(8*' ' + '<svg width="350" height="50">\n') line = 12*' ' + '<rect x="10" y="5" ' line += 'width="300" height="30" fill="lightGreen" />\n' file_object.write(line) line = 12*' ' + '<text x="15" y="24">' line += svg_tekst + '</text>\n' file_object.write(line) file_object.write(8*' ' + '</svg>\n') file_object.write(" </body>\n") file_object.write("</html>\n") file_object.close main()
Een dergelijke opzet van het programma werkt wel, maar het is behoorlijk onleesbaar. Bij elke regel moet je bedenken, wat voor effect die regel heeft als het programma wordt gedraaid. Ook later is het ontzettend lastig om je te herinneren wat dit programma ook al weer deed.
def html_start(html_title, file_object): file_object.write("<!DOCTYPE html>\n") file_object.write("<html>\n") file_object.write(4*" " + "<head>\n") file_object.write(8*" " + "<title>") file_object.write(html_title) file_object.write("</title>\n") file_object.write(4*" " + "</head>\n") file_object.write(4*" " + "<body>\n") def html_end(file_object): file_object.write(" </body>\n") file_object.write("</html>\n") def svg(svg_tekst, file_object): file_object.write(8*' ' + '<svg width="350" height="50">\n') line = 12*' ' + '<rect x="10" y="5" ' line += 'width="300" height="30" fill="lightGreen" />\n' file_object.write(line) line = 12*' ' + '<text x="15" y="24">' line += svg_tekst + '</text>\n' file_object.write(line) file_object.write(8*' ' + '</svg>\n') def main(): svg_tekst="the quick brown fox jumps over the lazy dog" html_title = "SVG rechthoek met tekst" file_object = open("outfile.html", "w") html_start(html_title, file_object) svg(svg_tekst, file_object) html_end(file_object) file_object.close if __name__ == '__main__': main()
➊ Met def-commando's worden eerst de functies html_start, html_end,svg en main aangemaakt. ➋ In de laatste regel van het programma wordt de functie main gestart. ➌ In de functie main worden eerst twee variabelen svg_tekst en html_title gevuld. ➍ Dan wordt er een file_object voor het bestand outfile.html gecreëerd. ➎ Op de volgende regels worden de functies html_start, svg en html_end uitgevoerd. Deze functies voegen de gewenste html- en svg-code toe aan het bestand outfile.html. ➏ Tenslotte wordt het file_object weer gesloten.
Dit programma genereert een html-file met de volgende inhoud:
<!DOCTYPE html> <html> <head> <title>SVG rechthoek met tekst</title> </head> <body> <svg width="350" height="50"> <rect x="10" y="5" width="300" height="30" fill="lightGreen" /> <text x="15" y="24">the quick brown fox jumps over the lazy dog</text> </svg> </body> </html>
Door een browser wordt dit svg-plaatje weergegeven als:
Ik wil een beter onderscheid tussen de regels die te maken hebben met het tekenen van een svg-plaatje in zijn geheel en het tekenen van een onderdeel van het plaatje namelijk de rechthoek. Ik splits daartoe de functie svg in drie delen: ➊ svg_start, ➋ rectangle en ➌ svg_end.
def html_start(html_title, file_object): file_object.write("<!DOCTYPE html>\n") file_object.write("<html>\n") file_object.write(4*" " + "<head>\n") file_object.write(8*" " + "<title>") file_object.write(html_title) file_object.write("</title>\n") file_object.write(4*" " + "</head>\n") file_object.write(4*" " + "<body>\n") def html_end(file_object): file_object.write(" </body>\n") file_object.write("</html>\n") def svg_start(file_object): line = 8*' ' + '<svg width="350" height="50">\n' file_object.write(line) def svg_end(file_object): file_object.write(8*' ' + '</svg>\n') def rectangle(svg_tekst, file_object): line = 12*' ' + '<rect x="10" y="5" ' line += 'width="300" height="30" ' line += 'fill="lightGreen" />\n' file_object.write(line) line = 12*' ' + '<text x="15" y="24">' line += svg_tekst + '</text>\n' file_object.write(line) def main(): svg_tekst="the quick brown fox jumps " svg_tekst += "over the lazy dog" html_title = "SVG rechthoek met tekst" file_object = open("outfile.html", "w") html_start(html_title, file_object) svg_start(file_object) rectangle(svg_tekst, file_object) svg_end(file_object) html_end(file_object) file_object.close if __name__ == '__main__': main()
De functies ➊ svg_start en ➋ svg_end hebben enkel te maken met een svg-plaatje, ongeacht wat daarin wordt getekend. De functie ➌ heeft enkel te maken met het tekenen van een rechthoek.
In de bovenstaande code gaan we uitzoeken in welke parameters vaste waarden zijn ingevuld. In het volgende zijn deze vaste waarden met rode letters aangegeven:
def html_start(html_title, file_object): file_object.write("<!DOCTYPE html>\n") file_object.write("<html>\n") file_object.write(4*" " + "<head>\n") file_object.write(8*" " + "<title>") file_object.write(html_title) file_object.write("</title>\n") file_object.write(4*" " + "</head>\n") file_object.write(4*" " + "<body>\n") def html_end(file_object): file_object.write(" </body>\n") file_object.write("</html>\n") def svg_start(file_object): line = 8*' ' + '<svg width="350" height="50">\n' file_object.write(line) def svg_end(file_object): file_object.write(8*' ' + '</svg>\n') def rectangle(svg_tekst, file_object): line = 12*' ' + '<rect x="10" y="5" ' line += 'width="300" height="30" ' line += 'fill="lightGreen" />\n' file_object.write(line) line = 12*' ' + '<text x="15" y="24">' line += svg_tekst + '</text>\n' file_object.write(line) def main(): svg_tekst="the quick brown fox jumps " svg_tekst += "over the lazy dog" html_title = "SVG rechthoek met tekst" file_object = open("outfile.html", "w") html_start(html_title, file_object) svg_start(file_object) rectangle(svg_tekst, file_object) svg_end(file_object) html_end(file_object) file_object.close if __name__ == '__main__': main()
➊ De output-file die dit programma genereert is de html-file met de naam outfile.html. Als je deze file met de browser laat zien, krijg je:
Ik geef in dit plaatje de waarden van een aantal van de parameters aan:
Ik wil deze rechthoek een andere plaats in het plaatje kunnen geven, en ik wil meer dan één rechthoek in een plaatje kunnen opnemen. Daarom verander ik de gevonden vast ingevulde waarden in parameters.
def html_start(html_title, file_object): file_object.write("<!DOCTYPE html>\n") file_object.write("<html>\n") file_object.write(4*" " + "<head>\n") file_object.write(8*" " + "<title>") file_object.write(html_title) file_object.write("</title>\n") file_object.write(4*" " + "</head>\n") file_object.write(4*" " + "<body>\n") def html_end(file_object): file_object.write(" </body>\n") file_object.write("</html>\n") def svg_start(width, height, file_object): line = 8*' ' + '<svg width="' + str(width) + '" ' line += 'height="' + str(height) + '">\n' file_object.write(line) def svg_end(file_object): file_object.write(8*' ' + '</svg>\n') def rectangle(rect_x, rect_y, width, height, rect_color, text_x, text_y, svg_tekst, file_object): line = 12*' ' + '<rect x="' + str(rect_x) + '" ' line += ' y="' + str(rect_y) + '" ' line += 'width="' + str(width) + '" ' line += 'height="' + str(height) + '" ' line += 'fill="' + rect_color + '" />\n' file_object.write(line) line = 12*' ' + '<text x="' + str(text_x) + '" ' line += 'y="' + str(text_y) + '">' line += svg_tekst + '</text>\n' file_object.write(line) def main(): svg_tekst = "the quick brown fox jumps " svg_tekst += "over the lazy dog" html_title = "SVG rechthoek met tekst" file_object = open("outfile.html", "w") html_start(html_title, file_object) svg_start(350, 50, file_object) rectangle(10, 5, 300, 30, "lightGreen", 15, 24, svg_tekst, file_object) svg_end(file_object) html_end(file_object) file_object.close if __name__ == '__main__': main()
➊ De waarden in de functies svg_start en rectangle werden toegekend, worden nu als parameter aan de functies doorgegeven vanuit de functie main.
In de functie main is het nu niet in één oogopslag duidelijk, wat de parameters in de aanroep van de functies svg_start en rectangle voorstellen. Ik ga daarom die vaste waarden opnemen in variabelen.
def html_start(html_title, file_object): file_object.write("<!DOCTYPE html>\n") file_object.write("<html>\n") file_object.write(4*" " + "<head>\n") file_object.write(8*" " + "<title>") file_object.write(html_title) file_object.write("</title>\n") file_object.write(4*" " + "</head>\n") file_object.write(4*" " + "<body>\n") def html_end(file_object): file_object.write(" </body>\n") file_object.write("</html>\n") def svg_start(width, height, file_object): line = 8*' ' + '<svg width="' + str(width) + '" ' line += 'height="' + str(height) + '">\n' file_object.write(line) def svg_end(file_object): file_object.write(8*' ' + '</svg>\n') def rectangle(rect_x, rect_y, width, height, rect_color, text_x, text_y, svg_tekst, file_object): line = 12*' ' + '<rect x="' + str(rect_x) + '" ' line += ' y="' + str(rect_y) + '" ' line += 'width="' + str(width) + '" ' line += 'height="' + str(height) + '" ' line += 'fill="' + rect_color + '" />\n' file_object.write(line) line = 12*' ' + '<text x="' + str(text_x) + '" ' line += 'y="' + str(text_y) + '">' line += svg_tekst + '</text>\n' file_object.write(line) def main(): html_title = "SVG rechthoek met tekst" svg_width = 350 svg_height = 50 rect_x = 10 rect_y = 5 rect_width = 300 rect_height = 30 rect_color = "lightGreen" rect_text_x = 15 rect_text_y = 24 rect_text="the quick brown fox jumps " rect_text += "over the lazy dog" file_object = open("outfile.html", "w") html_start(html_title, file_object) svg_start(svg_width, svg_height, file_object) rectangle(rect_x, rect_y, rect_width, rect_height, rect_color, rect_text_x, rect_text_y, rect_text, file_object) svg_end(file_object) html_end(file_object) file_object.close if __name__ == '__main__': main()
Het doel van het parametrizeren is, dat je het programma kunt gaan uitbreiden zodat je meer rechthoeken kunt gaan tekenen in één plaatje. Wat je daarmee introduceert is, dat je erop moet letten dat de parameters niet alle waarden kunnen aannemen. Zo kunnen de lengte en de breedte van een rechthoek nooit kleiner dan of gelijk aan nul zijn. In SVG geldt dat niet voor de plaats waar een figuur wordt getekend. De x- en y- waarden mogen wel kleiner of gelijk aan nul zijn.
We voegen een tweede svg-plaatje toe aan het html-bestand. Het tweede plaatje tekenen we 45 pixels onder het bestaande plaatje. Het svg-plaatje in zijn geheel moet dan iets grotere hoogte krijgen. De code wordt:
def html_start(html_title, file_object): file_object.write("<!DOCTYPE html>\n") file_object.write("<html>\n") file_object.write(4*" " + "<head>\n") file_object.write(8*" " + "<title>") file_object.write(html_title) file_object.write("</title>\n") file_object.write(4*" " + "</head>\n") file_object.write(4*" " + "<body>\n") def html_end(file_object): file_object.write(" </body>\n") file_object.write("</html>\n") def svg_start(width, height, file_object): line = 8*' ' + '<svg width="' + str(width) + '" ' line += 'height="' + str(height) + '">\n' file_object.write(line) def svg_end(file_object): file_object.write(8*' ' + '</svg>\n') def rectangle(rect_x, rect_y, width, height, rect_color, text_x, text_y, svg_tekst, file_object): line = 12*' ' + '<rect x="' + str(rect_x) + '" ' line += ' y="' + str(rect_y) + '" ' line += 'width="' + str(width) + '" ' line += 'height="' + str(height) + '" ' line += 'fill="' + rect_color + '" />\n' file_object.write(line) line = 12*' ' + '<text x="' + str(text_x) + '" ' line += 'y="' + str(text_y) + '">' line += svg_tekst + '</text>\n' file_object.write(line) def main(): html_title = "SVG rechthoeken met tekst" svg_width = 350 svg_height = 100 rect_x = 10 rect_y = 5 rect_width = 300 rect_height = 30 rect_color = "lightGreen" rect_text_x = 15 rect_text_y = 24 rect_text="the quick brown fox jumps " rect_text += "over the lazy dog" file_object = open("outfile.html", "w") html_start(html_title, file_object) svg_start(svg_width, svg_height, file_object) rectangle(rect_x, rect_y, rect_width, rect_height, rect_color, rect_text_x, rect_text_y, rect_text, file_object) rect_x = 10 rect_y = 50 rect_width = 300 rect_height = 30 rect_color = "lightGreen" rect_text_x = 15 rect_text_y = 69 rect_text="filmquiz bracht knappe ex-yogi" rect_text += " van de wijs" rectangle(rect_x, rect_y, rect_width, rect_height, rect_color, rect_text_x, rect_text_y, rect_text, file_object) svg_end(file_object) html_end(file_object) file_object.close if __name__ == '__main__': main()
Als je deze code uitvoert, wordt een bestand outfile.html gemaakt met de volgende inhoud:
<!DOCTYPE html> <html> <head> <title>SVG rechthoeken met tekst</title> </head> <body> <svg width="350" height="100"> <rect x="10" y="5" width="300" height="30" fill="lightGreen" /> <text x="15" y="24">the quick brown fox jumps over the lazy dog</text> <rect x="10" y="50" width="300" height="30" fill="lightGreen" /> <text x="15" y="69">filmquiz bracht knappe ex-yogi van de wijs</text> </svg> </body> </html>
In een browser wordt dit bestand als volgt weergegeven:
We zien in de main()-functie in bovenstaand programma min of meer zich herhalende code-blokken ontstaan. Het eerste code-blok bevat de statements om de eerste rechthoek weer te geven, het volgende code-blok bevat de statements die de tweede rechthoek weergeven. Dit soort zich herhalende code maakt het bewerkelijk om het programma in de toekomst aan te passen. De herhalende code moet je vermijden. Dit is het DRY-principe, dat staat voor Don't Repeat Yourself.
Ik wil de parameters die nodig zijn om de rechthoeken te definiëren kunnen inlezen vanuit een bestand. Het makkelijkste bestandstype dat ik ken om gegevens in op te slaan, is het csv-bestandstype. Ik maak het ASCII-bestand rechthoeken.csv aan met de volgende inhoud:
x;y;width;height;color;text_x;text_y;text; 10;5;300;30;lightGreen;15;24;the quick brown fox jumps over the lazy dog; 10;50;300;30;lightGreen;15;69;filmquiz bracht knappe ex-yogi van de wijs;
Ik wijzig vervolgens het programma. Ik lees de records uit het bestand rechthoeken.csv in, en neem deze records op in een list. Ik gebruik die list om de grootte van het plaatje te berekenen, en vervolgens het svg-bestand met het plaatje weg te schrijven.
def html_start(html_title, file_object): file_object.write("<!DOCTYPE html>\n") file_object.write("<html>\n") file_object.write(4*" " + "<head>\n") file_object.write(8*" " + "<title>") file_object.write(html_title) file_object.write("</title>\n") file_object.write(4*" " + "</head>\n") file_object.write(4*" " + "<body>\n") def html_end(file_object): file_object.write(" </body>\n") file_object.write("</html>\n") def svg_start(width, height, file_object): line = 8*' ' + '<svg width="' + str(width) + '" ' line += 'height="' + str(height) + '">\n' file_object.write(line) def svg_end(file_object): file_object.write(8*' ' + '</svg>\n') def rectangle(rect_x, rect_y, width, height, rect_color, text_x, text_y, svg_tekst, file_object): line = 12*' ' + '<rect x="' + str(rect_x) + '" ' line += ' y="' + str(rect_y) + '" ' line += 'width="' + str(width) + '" ' line += 'height="' + str(height) + '" ' line += 'fill="' + rect_color + '" />\n' file_object.write(line) line = 12*' ' + '<text x="' + str(text_x) + '" ' line += 'y="' + str(text_y) + '">' line += svg_tekst + '</text>\n' file_object.write(line) def main(): filename = 'rechthoeken.csv' rectangle_list = [] width_list = [] height_list = [] try: with open(filename) as f_input: for line in f_input: line = line.strip('\n') v = line.split(';') if v[0] != 'x': parameter_lijst = [ v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7] ] rectangle_list.append(parameter_lijst) width_list.append( int(v[0]) + int(v[2]) ) height_list.append( int(v[1]) + int(v[3]) ) except Exception as err: print( err ) html_title = "SVG rechthoeken met tekst" svg_width = max(width_list) svg_height = max(height_list) file_object = open("outfile.html", "w") html_start(html_title, file_object) svg_start(svg_width, svg_height, file_object) for r in rectangle_list: rectangle(r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], file_object) svg_end(file_object) html_end(file_object) file_object.close if __name__ == '__main__': main()
We willen nu een tekst in een rechthoek opnemen, die meerdere regels beslaat. We moeten er dan eerst achterkomen hoe breed en hoe hoog de rechthoek moet zijn, om ervoor te zorgen dat de tekst in de rechthoek past. We maken daartoe een kleine html-file, waarin die tekst voorkomt. Bijvoorbeeld:
<!DOCTYPE html> <html> <head> <title>Tekst</title> <style> .tekst { background-color: lightGreen; width: 20em; } </style> </head> <body> <div class="tekst"> Als beginnend concertist debuteerde een fijngevoelige gitarist, hierna improviseerden jeugdige klankkunstenaars levendig maar notenblind op Peruviaanse quena’s, robuuste slagwerkers trommelden uitzinnige volksmuziek, waarna xylofonisten "e;Yesterday"e; zongen. </div> </body> </html>
Als je deze html-file met een browser weergeeft, zie je:
Je kunt het groene tekstblok wat smaller of breder maken door de waarde van width binnen de style-tag te variëren. Als de grootte en de vorm van de rechthoek je bevalt, kun je kijken hoeveel pixels groot de groene rechthoek is. Je rechtsklikt in het browser-scherm, en kiest vervolgens de optie "Inspecteren". Bijvoorbeeld:
Beweeg dan zonder te klikken de muis over het groene gebied. (Dit heet op zijn engels: hoveren.) Dan verschijnt boven in beeld de grootte van de division (<div>) waarin de tekst zich bevindt.
In dit voorbeeld is dat 315,55 pixels breed en 107 pixels hoog. De breedte van 315,55 ronden we af naar 316.
De tekst wordt over 6 regels verdeeld. Een enkele regel heeft dan een hoogte van 107÷6=17,83333. We ronden dit af naar 18. Door die afronding wordt de totale hoogte van de rechthoek 6×18=108.
We moeten ons realiseren dat we zitten te kijken naar de kleinst mogelijke rechthoek waar de tekst in past. Die kleinste rechthoek noem ik een minimale rechthoek. Misschien willen we de rechthoek wat groter hebben in het plaatje dat we willen gaan maken. We willen marges om de tektst heen toevoegen aan de rechthoek. Deze marges zouden we kunnen aangeven met de woorden top, bottom, left en right.
We moeten ook aangeven waar de tekst wordt vervolgd op een nieuwe regel, want svg kent geen automatisch uitlijnen voor teksten.
Ik zie twee mogelijke manieren om de plaats van een rechthoek in een plaatje aan te geven. De ene manier is door de plaats van de linker bovenhoek te specificeren, de ander is het midden van de rechthoek te specificeren. Het midden is het snijpunt van de diagonalen. Als je het midden van de rechthoek specificeert, dan is de x-waarde voor de linker bovenhoek gelijk aan de x-waarde van het midden minus de halve breedte (width) van de minimale rechthoek minus left. De y-waarde van de linker bovenhoek is aan de y-waarde van het midden minus de halve hoogte (height) van de minimale rechthoek minus top.
We moeten ook uitrekenen waar de tekst in het plaatje moet worden geplaatst.